package com.ali.ttt.netty.cluster.config;

import com.ali.ttt.netty.cluster.handler.ClusterLoginRequestHandler;
import com.ali.ttt.netty.common.config.NettyServerProperties;
import com.ali.ttt.netty.common.handler.InMessageHandler;
import com.ali.ttt.netty.server.handler.LoginRequestHandler;
import com.ali.ttt.netty.server.handler.ServerExceptionHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.json.JsonObjectDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;

/**
 * @Author : yuxl
 * @create 2024/7/1 10:59 AM
 */
@Slf4j
@Data
@Component
public class NettyCluster {

    // 创建连接监听reactor 轮询组
    private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    // 创建连接处理reactor 轮询组
    private final EventLoopGroup workerGroup = new NioEventLoopGroup();

    @Resource
    private NettyClusterProperties nettyClusterProperties;

    @Resource
    private InMessageHandler inMessageHandler;

    @Resource
    private ClusterLoginRequestHandler clusterLoginRequestHandler;
    @Resource
    private ServerExceptionHandler serverExceptionHandler;

    @Resource
    private Cluster cluster;

    @Async("async-thread-pool")
    public void start() throws UnknownHostException {
        try {
            //服务端启动引导实例
            ServerBootstrap b = new ServerBootstrap();
            //1 设置reactor 线程
            b.group(bossGroup, workerGroup)
                    //2 设置nio类型的channel
                    .channel(NioServerSocketChannel.class)
                    //3 设置日志
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .localAddress(new InetSocketAddress(nettyClusterProperties.getProd()))
                    //4 设置通道选项
//                    .option(ChannelOption.ALLOCATOR,   PooledByteBufAllocator.DEFAULT)
//                    .childOption(ChannelOption.ALLOCATOR,   PooledByteBufAllocator.DEFAULT)
            ;
            //5 装配流水线
            b.childHandler(new ChannelInitializer<SocketChannel>() {
                //有连接到达时会创建一个channel
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new JsonObjectDecoder());
                    ch.pipeline().addLast(clusterLoginRequestHandler);
                    ch.pipeline().addLast(inMessageHandler);
                    ch.pipeline().addLast(serverExceptionHandler);
//                    ch.pipeline().addLast(loginRequestHandler);
                }
            });
            // 6 开始绑定server
            // 通过调用sync同步方法阻塞直到绑定成功
            ChannelFuture ch = b.bind().sync();
            log.info("\n----------------------------------------------------------\n\t" +
                            "netty服务端[集群] 启动成功\n\t" +
                            "Local: \t\t\033[1;34mlocalhost:{}\033[0m\n\t" +
                            "External: \t\033[1;34m{}:{}\033[0m\n\t" +
                            "Doc: \t\t\033[1;34m{}:{}\033[0m\n" +
                            "----------------------------------------------------------",
                    nettyClusterProperties.getProd(),
                    InetAddress.getLocalHost().getHostAddress(),
                    nettyClusterProperties.getProd(),
                    InetAddress.getLocalHost().getHostAddress(),
                    nettyClusterProperties.getProd()
            );
            cluster.registration();
            // 7 监听通道关闭事件
            // 应用程序会一直等待，直到channel关闭
            ChannelFuture closeFuture = ch.channel().closeFuture();
            closeFuture.sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            log.info("WebSocket服务关闭");
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
            cluster.logout();
        }
    }

    @PreDestroy
    public void cleanup() throws UnknownHostException {
        log.info("[jvm]WebSocket服务关闭");
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
        cluster.logout();
    }
}
